package com.simple.system.aspect;

import com.alibaba.fastjson2.JSON;
import com.simple.common.utils.IpUtils;
import com.simple.framework.core.auth.JwtContext;
import com.simple.system.annotation.SystemLog;
import com.simple.system.domain.entity.SysSystemLog;
import com.simple.system.enums.BusinessStatus;
import com.simple.system.service.SysSystemLogService;
import com.simple.system.utils.UserAgentParser;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Description 系统日志记录处理
 * @Author xiaoke
 * @Date 2024/8/10 18:00
 * @Version 1.0
 */
@Aspect
@Component
public class SystemLogAspect {

    private static final Logger log = LoggerFactory.getLogger(SystemLogAspect.class);

    private final HttpServletRequest httpServletRequest;
    private final HttpServletResponse httpServletResponse;

    private final SysSystemLogService systemLogService;

    /**
     * 计算操作消耗时间
     */
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");

    public SystemLogAspect(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, SysSystemLogService systemLogService) {
        this.httpServletRequest = httpServletRequest;
        this.httpServletResponse = httpServletResponse;
        this.systemLogService = systemLogService;
    }

    /**
     * 处理请求前执行
     */
    @Before(value = "@annotation(controllerLog)")
    public void boBefore(JoinPoint joinPoint, SystemLog controllerLog) {
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }


    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, SystemLog controllerLog, Object jsonResult) {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, SystemLog controllerLog, Exception e) {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, SystemLog controllerLog, final Exception e, Object jsonResult) {
        try {
            SysSystemLog systemLog = new SysSystemLog();
            systemLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            String ip = IpUtils.getIpAddr(httpServletRequest);
            systemLog.setOperIp(ip);
            // 设置IP地址
            String[] IpAddr = IpUtils.parseIpAddr(ip);
            StringBuilder ipAddress = new StringBuilder();
            for (String s : IpAddr) {
                ipAddress.append(s);
            }
            systemLog.setIpAddress(ipAddress.toString());

            //是否异常
            if (e != null) {
                systemLog.setStatus(BusinessStatus.FAIL.ordinal());
                systemLog.setErrorMsg(e.toString());
            }

            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            systemLog.setMethod(className + "." + methodName + "()");
            // 设置操作人员
            systemLog.setOperName(JwtContext.getUsername());
            // 设置请求方式
            systemLog.setRequestMethod(httpServletRequest.getMethod());
            // 请求url
            String requestUrl = httpServletRequest.getRequestURL().toString();
            String contextPath = httpServletRequest.getContextPath();
            String path = requestUrl.substring(requestUrl.indexOf(contextPath) + contextPath.length());
            systemLog.setOperUrl(contextPath + path);
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, systemLog, jsonResult);
            // 设置消耗时间
            systemLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
            //保存数据库
            systemLogService.create(systemLog);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        } finally {
            TIME_THREADLOCAL.remove();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log       日志
     * @param systemLog 系统日志
     * @throws Exception 异常信息
     */
    private void getControllerMethodDescription(JoinPoint joinPoint, SystemLog log, SysSystemLog systemLog, Object jsonResult) throws Exception {
        // 设置action动作
        systemLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        systemLog.setTitle(log.title());
        // 设置操作人类别
        systemLog.setOperatorType(log.operatorType().ordinal());
        // 设置请求头
        systemLog.setRequestHeader(getRequestHeader());
        // 设置浏览器类型
        UserAgentParser.UserAgentInfo userAgentInfo = UserAgentParser.parseUserAgent(httpServletRequest);
        systemLog.setBrowserType(userAgentInfo.getBrowser());
        systemLog.setOperatingSystem(userAgentInfo.getOs());
        // 是否需要保存request，参数和值
        if (log.isSaveRequestData()) {
            Object[] args = joinPoint.getArgs();
            // 参数
            String params = JSON.toJSONString(args);
            // 获取参数的信息，传入到数据库中。
            systemLog.setRequestBody(params);
        }
        // 是否需要保存response，参数和值
        if (log.isSaveResponseData() && jsonResult != null) {
            systemLog.setResponseBody(JSON.toJSONString(jsonResult));
        }
    }

    /**
     * 获取请求头
     *
     * @return String
     */
    private String getRequestHeader() {
        Map<String, String> headerMap = new LinkedHashMap<>();
        Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            //过滤token
            if (headerName.equals("Authorization".toLowerCase())) continue;
            String headerValue = httpServletRequest.getHeader(headerName);
            headerMap.put(headerName, headerValue);
        }
        return JSON.toJSONString(headerMap);
    }


}
